﻿using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Linq;
using Castle.DynamicProxy;
using InterfaceLiftLib.Converters.Json;
using InterfaceLiftLib.Service.Interfaces;
using Newtonsoft.Json;

namespace InterfaceLiftLib.Interceptors
{
    [Serializable]
    public class InterfaceLiftServiceInterceptor : IInterceptor
    {        
        private IHttpWebRequestBuilder m_webRequestBuilder;
        private readonly MethodInfo m_executeRequestInfo;

        public InterfaceLiftServiceInterceptor(IHttpWebRequestBuilder webRequestBuilder)
        {
            if (webRequestBuilder == null)
                throw new ArgumentNullException("webRequestBuilder");

            m_webRequestBuilder = webRequestBuilder;
            m_executeRequestInfo = this.GetType().GetMethod("ExecuteRequest", BindingFlags.NonPublic | BindingFlags.Instance);
        }

        public void Intercept(IInvocation invocation)
        {
            //getting the callback and the callback generic type..
            var callback = invocation.Arguments.Last();
            var callbackGenericType = callback != null
                                          ? callback.GetType().GetGenericArguments().First()
                                          : typeof (object);
            
            //method parameters
            var wsParams = invocation.Arguments.Take(invocation.Arguments.Count() - 1).ToArray();

            //building and invoking the MethodInfo object that targets to ExecuteRequest method..
            var executeRequestMethodInfo = m_executeRequestInfo.MakeGenericMethod(callbackGenericType);
            var methodInvocationParameters = new[] { invocation.Method, callback, wsParams };
            executeRequestMethodInfo.Invoke(this, methodInvocationParameters);
        }

        private void ExecuteRequest<T>(MethodInfo methodInfo, Action<T, Exception> callback, object[] args) where T : class
        {
            Task.Factory
                .StartNew<T>(() =>
                                 {
                                     var request = m_webRequestBuilder.BuildRequest(methodInfo, args);
                                     using (var response = request.GetResponse())
                                     {
                                         using (var streamReader = new StreamReader(response.GetResponseStream()))
                                         {
                                             var json = streamReader.ReadToEnd();
                                             var serializer = new JsonSerializer();
                                             serializer.Converters.Add(new SafeNumberConverter());
                                             var jsonReader = new JsonTextReader(new StringReader(json));
                                             var result = serializer.Deserialize<T>(jsonReader);
                                             return result;
                                         }
                                     }
                                 })
                .ContinueWith(task =>
                                  {
                                      if (callback != null)
                                      {
                                          var result = task.IsFaulted ? null: task.Result;
                                          var lastException = task.Exception != null ? task.Exception.InnerException : null;
                                          callback(result, lastException);
                                      }
                                  });
        }
    }
}
